home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Unix / Shells / tcsh / Source / sh.func.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-21  |  43.0 KB  |  2,074 lines

  1. /* $Header: /u/christos/src/tcsh-6.03/RCS/sh.func.c,v 3.42 1992/11/13 04:19:10 christos Exp $ */
  2. /*
  3.  * sh.func.c: csh builtin functions
  4.  */
  5. /*-
  6.  * Copyright (c) 1980, 1991 The Regents of the University of California.
  7.  * All rights reserved.
  8.  *
  9.  * Redistribution and use in source and binary forms, with or without
  10.  * modification, are permitted provided that the following conditions
  11.  * are met:
  12.  * 1. Redistributions of source code must retain the above copyright
  13.  *    notice, this list of conditions and the following disclaimer.
  14.  * 2. Redistributions in binary form must reproduce the above copyright
  15.  *    notice, this list of conditions and the following disclaimer in the
  16.  *    documentation and/or other materials provided with the distribution.
  17.  * 3. All advertising materials mentioning features or use of this software
  18.  *    must display the following acknowledgement:
  19.  *    This product includes software developed by the University of
  20.  *    California, Berkeley and its contributors.
  21.  * 4. Neither the name of the University nor the names of its contributors
  22.  *    may be used to endorse or promote products derived from this software
  23.  *    without specific prior written permission.
  24.  *
  25.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  26.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  27.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  28.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  29.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  30.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  31.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  33.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  34.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  35.  * SUCH DAMAGE.
  36.  */
  37. #include "sh.h"
  38.  
  39. RCSID("$Id: sh.func.c,v 3.42 1992/11/13 04:19:10 christos Exp $")
  40.  
  41. #include "ed.h"
  42. #include "tw.h"
  43. #include "tc.h"
  44.  
  45. /*
  46.  * C shell
  47.  */
  48. extern int just_signaled;
  49. extern char **environ;
  50.  
  51. extern bool MapsAreInited;
  52. extern bool NLSMapsAreInited;
  53. extern bool NoNLSRebind;
  54. extern bool GotTermCaps;
  55.  
  56. static int zlast = -1;
  57.  
  58. static    void    islogin        __P((void));
  59. static    void    reexecute    __P((struct command *));
  60. static    void    preread        __P((void));
  61. static    void    doagain        __P((void));
  62. static  char   *isrchx        __P((int));
  63. static    void    search        __P((int, int, Char *));
  64. static    int    getword        __P((Char *));
  65. static    int    keyword        __P((Char *));
  66. static    void    toend        __P((void));
  67. static    void    xecho        __P((int, Char **));
  68.  
  69. struct biltins *
  70. isbfunc(t)
  71.     struct command *t;
  72. {
  73.     register Char *cp = t->t_dcom[0];
  74.     register struct biltins *bp, *bp1, *bp2;
  75.     static struct biltins label = {"", dozip, 0, 0};
  76.     static struct biltins foregnd = {"%job", dofg1, 0, 0};
  77.     static struct biltins backgnd = {"%job &", dobg1, 0, 0};
  78.  
  79.     if (*cp != ':' && lastchr(cp) == ':') {
  80.     label.bname = short2str(cp);
  81.     return (&label);
  82.     }
  83.     if (*cp == '%') {
  84.     if (t->t_dflg & F_AMPERSAND) {
  85.         t->t_dflg &= ~F_AMPERSAND;
  86.         backgnd.bname = short2str(cp);
  87.         return (&backgnd);
  88.     }
  89.     foregnd.bname = short2str(cp);
  90.     return (&foregnd);
  91.     }
  92. #ifdef WARP
  93.     /*
  94.      * This is a perhaps kludgy way to determine if the warp builtin is to be
  95.      * acknowledged or not.  If checkwarp() fails, then we are to assume that
  96.      * the warp command is invalid, and carry on as we would handle any other
  97.      * non-builtin command.         -- JDK 2/4/88
  98.      */
  99.     if (eq(STRwarp, cp) && !checkwarp()) {
  100.     return (0);        /* this builtin disabled */
  101.     }
  102. #endif /* WARP */
  103.     /*
  104.      * Binary search Bp1 is the beginning of the current search range. Bp2 is
  105.      * one past the end.
  106.      */
  107.     for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2;) {
  108.     register i;
  109.  
  110.     bp = bp1 + ((bp2 - bp1) >> 1);
  111.     if ((i = *cp - *bp->bname) == 0 &&
  112.         (i = Strcmp(cp, str2short(bp->bname))) == 0)
  113.         return bp;
  114.     if (i < 0)
  115.         bp2 = bp;
  116.     else
  117.         bp1 = bp + 1;
  118.     }
  119.     return (0);
  120. }
  121.  
  122. void
  123. func(t, bp)
  124.     register struct command *t;
  125.     register struct biltins *bp;
  126. {
  127.     int     i;
  128.  
  129.     xechoit(t->t_dcom);
  130.     setname(bp->bname);
  131.     i = blklen(t->t_dcom) - 1;
  132.     if (i < bp->minargs)
  133.     stderror(ERR_NAME | ERR_TOOFEW);
  134.     if (i > bp->maxargs)
  135.     stderror(ERR_NAME | ERR_TOOMANY);
  136.     (*bp->bfunct) (t->t_dcom, t);
  137. }
  138.  
  139. /*ARGSUSED*/
  140. void
  141. doonintr(v, c)
  142.     Char  **v;
  143.     struct command *c;
  144. {
  145.     register Char *cp;
  146.     register Char *vv = v[1];
  147.  
  148.     if (parintr == SIG_IGN)
  149.     return;
  150.     if (setintr && intty)
  151.     stderror(ERR_NAME | ERR_TERMINAL);
  152.     cp = gointr;
  153.     gointr = 0;
  154.     xfree((ptr_t) cp);
  155.     if (vv == 0) {
  156. #ifdef BSDSIGS
  157.     if (setintr) {
  158.         (void) sigblock(sigmask(SIGINT));
  159.         (void) signal(SIGINT, pintr);
  160.     }
  161.     else 
  162.         (void) signal(SIGINT, SIG_DFL);
  163. #else /* !BSDSIGS */
  164.     if (setintr) {
  165.         (void) sighold(SIGINT);
  166.         (void) sigset(SIGINT, pintr);
  167.     }
  168.     else
  169.         (void) sigset(SIGINT, SIG_DFL);
  170. #endif /* BSDSIGS */
  171.     gointr = 0;
  172.     }
  173.     else if (eq((vv = strip(vv)), STRminus)) {
  174. #ifdef BSDSIGS
  175.     (void) signal(SIGINT, SIG_IGN);
  176. #else /* !BSDSIGS */
  177.     (void) sigset(SIGINT, SIG_IGN);
  178. #endif /* BSDSIGS */
  179.     gointr = Strsave(STRminus);
  180.     }
  181.     else {
  182.     gointr = Strsave(vv);
  183. #ifdef BSDSIGS
  184.     (void) signal(SIGINT, pintr);
  185. #else /* !BSDSIGS */
  186.     (void) sigset(SIGINT, pintr);
  187. #endif /* BSDSIGS */
  188.     }
  189. }
  190.  
  191. /*ARGSUSED*/
  192. void
  193. donohup(v, c)
  194.     Char **v;
  195.     struct command *c;
  196. {
  197.     if (intty)
  198.     stderror(ERR_NAME | ERR_TERMINAL);
  199.     if (setintr == 0) {
  200.     (void) signal(SIGHUP, SIG_IGN);
  201. #ifdef CC
  202.     submit(getpid());
  203. #endif /* CC */
  204.     }
  205. }
  206.  
  207. /*ARGSUSED*/
  208. void
  209. dozip(v, c)
  210.     Char **v;
  211.     struct command *c;
  212. {
  213.     ;
  214. }
  215.  
  216. void
  217. prvars()
  218. {
  219.     plist(&shvhed);
  220. }
  221.  
  222. /*ARGSUSED*/
  223. void
  224. doalias(v, c)
  225.     register Char **v;
  226.     struct command *c;
  227. {
  228.     register struct varent *vp;
  229.     register Char *p;
  230.  
  231.     v++;
  232.     p = *v++;
  233.     if (p == 0)
  234.     plist(&aliases);
  235.     else if (*v == 0) {
  236.     vp = adrof1(strip(p), &aliases);
  237.     if (vp)
  238.         blkpr(vp->vec), xputchar('\n');
  239.     }
  240.     else {
  241.     if (eq(p, STRalias) || eq(p, STRunalias)) {
  242.         setname(short2str(p));
  243.         stderror(ERR_NAME | ERR_DANGER);
  244.     }
  245.     set1(strip(p), saveblk(v), &aliases);
  246.     tw_cmd_free();
  247.     }
  248. }
  249.  
  250. /*ARGSUSED*/
  251. void
  252. unalias(v, c)
  253.     Char  **v;
  254.     struct command *c;
  255. {
  256.     unset1(v, &aliases);
  257.     tw_cmd_free();
  258. }
  259.  
  260. /*ARGSUSED*/
  261. void
  262. dologout(v, c)
  263.     Char **v;
  264.     struct command *c;
  265. {
  266.     islogin();
  267.     goodbye(NULL, NULL);
  268. }
  269.  
  270. /*ARGSUSED*/
  271. void
  272. dologin(v, c)
  273.     Char  **v;
  274.     struct command *c;
  275. {
  276.     islogin();
  277.     rechist(NULL);
  278.     (void) signal(SIGTERM, parterm);
  279.     (void) execl(_PATH_LOGIN, "login", short2str(v[1]), NULL);
  280.     untty();
  281.     xexit(1);
  282. }
  283.  
  284.  
  285. #ifdef NEWGRP
  286. /*ARGSUSED*/
  287. void
  288. donewgrp(v, c)
  289.     Char  **v;
  290.     struct command *c;
  291. {
  292.     char **p;
  293.     if (chkstop == 0 && setintr)
  294.     panystop(0);
  295.     (void) signal(SIGTERM, parterm);
  296.     p = short2blk(&v[1]);
  297.     /*
  298.      * From Beto Appleton (beto@aixwiz.austin.ibm.com)
  299.      * Newgrp can take 2 arguments...
  300.      */
  301.     (void) execv(_PATH_BIN_NEWGRP, "newgrp", p, NULL);
  302.     (void) execv(_PATH_USRBIN_NEWGRP, "newgrp", p, NULL);
  303.     blkfree((Char **) p);
  304.     untty();
  305.     xexit(1);
  306. }
  307. #endif /* NEWGRP */
  308.  
  309. static void
  310. islogin()
  311. {
  312.     if (chkstop == 0 && setintr)
  313.     panystop(0);
  314.     if (loginsh)
  315.     return;
  316.     stderror(ERR_NOTLOGIN);
  317. }
  318.  
  319. void
  320. doif(v, kp)
  321.     Char  **v;
  322.     struct command *kp;
  323. {
  324.     register int i;
  325.     register Char **vv;
  326.  
  327.     v++;
  328.     i = expr(&v);
  329.     vv = v;
  330.     if (*vv == NULL)
  331.     stderror(ERR_NAME | ERR_EMPTYIF);
  332.     if (eq(*vv, STRthen)) {
  333.     if (*++vv)
  334.         stderror(ERR_NAME | ERR_IMPRTHEN);
  335.     setname(short2str(STRthen));
  336.     /*
  337.      * If expression was zero, then scan to else , otherwise just fall into
  338.      * following code.
  339.      */
  340.     if (!i)
  341.         search(TC_IF, 0, NULL);
  342.     return;
  343.     }
  344.     /*
  345.      * Simple command attached to this if. Left shift the node in this tree,
  346.      * munging it so we can reexecute it.
  347.      */
  348.     if (i) {
  349.     lshift(kp->t_dcom, vv - kp->t_dcom);
  350.     reexecute(kp);
  351.     donefds();
  352.     }
  353. }
  354.  
  355. /*
  356.  * Reexecute a command, being careful not
  357.  * to redo i/o redirection, which is already set up.
  358.  */
  359. static void
  360. reexecute(kp)
  361.     register struct command *kp;
  362. {
  363.     kp->t_dflg &= F_SAVE;
  364.     kp->t_dflg |= F_REPEAT;
  365.     /*
  366.      * If tty is still ours to arbitrate, arbitrate it; otherwise dont even set
  367.      * pgrp's as the jobs would then have no way to get the tty (we can't give
  368.      * it to them, and our parent wouldn't know their pgrp, etc.
  369.      */
  370.     execute(kp, (tpgrp > 0 ? tpgrp : -1), NULL, NULL);
  371. }
  372.  
  373. /*ARGSUSED*/
  374. void
  375. doelse (v, c)
  376.     Char **v;
  377.     struct command *c;
  378. {
  379.     search(TC_ELSE, 0, NULL);
  380. }
  381.  
  382. /*ARGSUSED*/
  383. void
  384. dogoto(v, c)
  385.     Char  **v;
  386.     struct command *c;
  387. {
  388.     Char   *lp;
  389.  
  390.     gotolab(lp = globone(v[1], G_ERROR));
  391.     xfree((ptr_t) lp);
  392. }
  393.  
  394. void
  395. gotolab(lab)
  396.     Char *lab;
  397. {
  398.     register struct whyle *wp;
  399.     /*
  400.      * While we still can, locate any unknown ends of existing loops. This
  401.      * obscure code is the WORST result of the fact that we don't really parse.
  402.      */
  403.     zlast = TC_GOTO;
  404.     for (wp = whyles; wp; wp = wp->w_next)
  405.     if (wp->w_end.type == F_SEEK && wp->w_end.f_seek == 0) {
  406.         search(TC_BREAK, 0, NULL);
  407.         btell(&wp->w_end);
  408.     }
  409.     else {
  410.         bseek(&wp->w_end);
  411.     }
  412.     search(TC_GOTO, 0, lab);
  413.     /*
  414.      * Eliminate loops which were exited.
  415.      */
  416.     wfree();
  417. }
  418.  
  419. /*ARGSUSED*/
  420. void
  421. doswitch(v, c)
  422.     register Char **v;
  423.     struct command *c;
  424. {
  425.     register Char *cp, *lp;
  426.  
  427.     v++;
  428.     if (!*v || *(*v++) != '(')
  429.     stderror(ERR_SYNTAX);
  430.     cp = **v == ')' ? STRNULL : *v++;
  431.     if (*(*v++) != ')')
  432.     v--;
  433.     if (*v)
  434.     stderror(ERR_SYNTAX);
  435.     search(TC_SWITCH, 0, lp = globone(cp, G_ERROR));
  436.     xfree((ptr_t) lp);
  437. }
  438.  
  439. /*ARGSUSED*/
  440. void
  441. dobreak(v, c)
  442.     Char **v;
  443.     struct command *c;
  444. {
  445.     if (whyles)
  446.     toend();
  447.     else
  448.     stderror(ERR_NAME | ERR_NOTWHILE);
  449. }
  450.  
  451. /*ARGSUSED*/
  452. void
  453. doexit(v, c)
  454.     Char  **v;
  455.     struct command *c;
  456. {
  457.     if (chkstop == 0 && (intty || intact) && evalvec == 0)
  458.     panystop(0);
  459.     /*
  460.      * Don't DEMAND parentheses here either.
  461.      */
  462.     v++;
  463.     if (*v) {
  464.     set(STRstatus, putn(expr(&v)));
  465.     if (*v)
  466.         stderror(ERR_NAME | ERR_EXPRESSION);
  467.     }
  468.     btoeof();
  469.     if (intty)
  470.     (void) close(SHIN);
  471. }
  472.  
  473. /*ARGSUSED*/
  474. void
  475. doforeach(v, c)
  476.     register Char **v;
  477.     struct command *c;
  478. {
  479.     register Char *cp, *sp;
  480.     register struct whyle *nwp;
  481.  
  482.     v++;
  483.     sp = cp = strip(*v);
  484.     if (!letter(*sp))
  485.     stderror(ERR_NAME | ERR_VARBEGIN);
  486.     while (*cp && alnum(*cp))
  487.     cp++;
  488.     if (*cp)
  489.     stderror(ERR_NAME | ERR_VARALNUM);
  490.     if ((cp - sp) > MAXVARLEN)
  491.     stderror(ERR_NAME | ERR_VARTOOLONG);
  492.     cp = *v++;
  493.     if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')')
  494.     stderror(ERR_NAME | ERR_NOPAREN);
  495.     v++;
  496.     gflag = 0, tglob(v);
  497.     v = globall(v);
  498.     if (v == 0)
  499.     stderror(ERR_NAME | ERR_NOMATCH);
  500.     nwp = (struct whyle *) xcalloc(1, sizeof *nwp);
  501.     nwp->w_fe = nwp->w_fe0 = v;
  502.     gargv = 0;
  503.     btell(&nwp->w_start);
  504.     nwp->w_fename = Strsave(cp);
  505.     nwp->w_next = whyles;
  506.     nwp->w_end.type = F_SEEK;
  507.     whyles = nwp;
  508.     /*
  509.      * Pre-read the loop so as to be more comprehensible to a terminal user.
  510.      */
  511.     zlast = TC_FOREACH;
  512.     if (intty)
  513.     preread();
  514.     doagain();
  515. }
  516.  
  517. /*ARGSUSED*/
  518. void
  519. dowhile(v, c)
  520.     Char  **v;
  521.     struct command *c;
  522. {
  523.     register int status;
  524.     register bool again = whyles != 0 && SEEKEQ(&whyles->w_start, &lineloc) &&
  525.     whyles->w_fename == 0;
  526.  
  527.     v++;
  528.     /*
  529.      * Implement prereading here also, taking care not to evaluate the
  530.      * expression before the loop has been read up from a terminal.
  531.      */
  532.     if (intty && !again)
  533.     status = !exp0(&v, 1);
  534.     else
  535.     status = !expr(&v);
  536.     if (*v)
  537.     stderror(ERR_NAME | ERR_EXPRESSION);
  538.     if (!again) {
  539.     register struct whyle *nwp =
  540.     (struct whyle *) xcalloc(1, sizeof(*nwp));
  541.  
  542.     nwp->w_start = lineloc;
  543.     nwp->w_end.type = F_SEEK;
  544.     nwp->w_end.f_seek = 0;
  545.     nwp->w_next = whyles;
  546.     whyles = nwp;
  547.     zlast = TC_WHILE;
  548.     if (intty) {
  549.         /*
  550.          * The tty preread
  551.          */
  552.         preread();
  553.         doagain();
  554.         return;
  555.     }
  556.     }
  557.     if (status)
  558.     /* We ain't gonna loop no more, no more! */
  559.     toend();
  560. }
  561.  
  562. static void
  563. preread()
  564. {
  565.     whyles->w_end.type = I_SEEK;
  566.     if (setintr)
  567. #ifdef BSDSIGS
  568.     (void) sigsetmask(sigblock((sigmask_t) 0) & ~sigmask(SIGINT));
  569. #else /* !BSDSIGS */
  570.     (void) sigrelse (SIGINT);
  571. #endif /* BSDSIGS */
  572.     search(TC_BREAK, 0, NULL);        /* read the expression in */
  573.     if (setintr)
  574. #ifdef BSDSIGS
  575.     (void) sigblock(sigmask(SIGINT));
  576. #else /* !BSDSIGS */
  577.     (void) sighold(SIGINT);
  578. #endif /* BSDSIGS */
  579.     btell(&whyles->w_end);
  580. }
  581.  
  582. /*ARGSUSED*/
  583. void
  584. doend(v, c)
  585.     Char **v;
  586.     struct command *c;
  587. {
  588.     if (!whyles)
  589.     stderror(ERR_NAME | ERR_NOTWHILE);
  590.     btell(&whyles->w_end);
  591.     doagain();
  592. }
  593.  
  594. /*ARGSUSED*/
  595. void
  596. docontin(v, c)
  597.     Char **v;
  598.     struct command *c;
  599. {
  600.     if (!whyles)
  601.     stderror(ERR_NAME | ERR_NOTWHILE);
  602.     doagain();
  603. }
  604.  
  605. static void
  606. doagain()
  607. {
  608.     /* Repeating a while is simple */
  609.     if (whyles->w_fename == 0) {
  610.     bseek(&whyles->w_start);
  611.     return;
  612.     }
  613.     /*
  614.      * The foreach variable list actually has a spurious word ")" at the end of
  615.      * the w_fe list.  Thus we are at the of the list if one word beyond this
  616.      * is 0.
  617.      */
  618.     if (!whyles->w_fe[1]) {
  619.     dobreak(NULL, NULL);
  620.     return;
  621.     }
  622.     set(whyles->w_fename, Strsave(*whyles->w_fe++));
  623.     bseek(&whyles->w_start);
  624. }
  625.  
  626. void
  627. dorepeat(v, kp)
  628.     Char  **v;
  629.     struct command *kp;
  630. {
  631.     register int i;
  632.  
  633. #ifdef BSDSIGS
  634.     register sigmask_t omask = 0;
  635.  
  636. #endif /* BSDSIGS */
  637.  
  638.     i = getn(v[1]);
  639.     if (setintr)
  640. #ifdef BSDSIGS
  641.     omask = sigblock(sigmask(SIGINT)) & ~sigmask(SIGINT);
  642. #else /* !BSDSIGS */
  643.     (void) sighold(SIGINT);
  644. #endif /* BSDSIGS */
  645.     lshift(v, 2);
  646.     while (i > 0) {
  647.     if (setintr)
  648. #ifdef BSDSIGS
  649.         (void) sigsetmask(omask);
  650. #else /* !BSDSIGS */
  651.         (void) sigrelse (SIGINT);
  652. #endif /* BSDSIGS */
  653.     reexecute(kp);
  654.     --i;
  655.     }
  656.     donefds();
  657.     if (setintr)
  658. #ifdef BSDSIGS
  659.     (void) sigsetmask(omask);
  660. #else /* !BSDSIGS */
  661.     (void) sigrelse (SIGINT);
  662. #endif /* BSDSIGS */
  663. }
  664.  
  665. /*ARGSUSED*/
  666. void
  667. doswbrk(v, c)
  668.     Char **v;
  669.     struct command *c;
  670. {
  671.     search(TC_BRKSW, 0, NULL);
  672. }
  673.  
  674. int
  675. srchx(cp)
  676.     register Char *cp;
  677. {
  678.     register struct srch *sp, *sp1, *sp2;
  679.     register i;
  680.  
  681.     /*
  682.      * Binary search Sp1 is the beginning of the current search range. Sp2 is
  683.      * one past the end.
  684.      */
  685.     for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2;) {
  686.     sp = sp1 + ((sp2 - sp1) >> 1);
  687.     if ((i = *cp - *sp->s_name) == 0 &&
  688.         (i = Strcmp(cp, str2short(sp->s_name))) == 0)
  689.         return sp->s_value;
  690.     if (i < 0)
  691.         sp2 = sp;
  692.     else
  693.         sp1 = sp + 1;
  694.     }
  695.     return (-1);
  696. }
  697.  
  698. static char *
  699. isrchx(n)
  700.     register int n;
  701. {
  702.     register struct srch *sp, *sp2;
  703.  
  704.     for (sp = srchn, sp2 = srchn + nsrchn; sp < sp2; sp++)
  705.     if (sp->s_value == n)
  706.         return (sp->s_name);
  707.     return ("");
  708. }
  709.  
  710.  
  711. static Char Stype;
  712. static Char *Sgoal;
  713.  
  714. static void
  715. search(type, level, goal)
  716.     int     type;
  717.     register int level;
  718.     Char   *goal;
  719. {
  720.     Char    wordbuf[BUFSIZE];
  721.     register Char *aword = wordbuf;
  722.     register Char *cp;
  723.  
  724.     Stype = type;
  725.     Sgoal = goal;
  726.     if (type == TC_GOTO) {
  727.     struct Ain a;
  728.     a.type = F_SEEK;
  729.     a.f_seek = 0;
  730.     bseek(&a);
  731.     }
  732.     do {
  733.     if (intty && fseekp == feobp && aret == F_SEEK)
  734.         printprompt(1, isrchx(type == TC_BREAK ? zlast : type));
  735.     /* xprintf("? "), flush(); */
  736.     aword[0] = 0;
  737.     (void) getword(aword);
  738.     switch (srchx(aword)) {
  739.  
  740.     case TC_ELSE:
  741.         if (level == 0 && type == TC_IF)
  742.         return;
  743.         break;
  744.  
  745.     case TC_IF:
  746.         while (getword(aword))
  747.         continue;
  748.         if ((type == TC_IF || type == TC_ELSE) &&
  749.         eq(aword, STRthen))
  750.         level++;
  751.         break;
  752.  
  753.     case TC_ENDIF:
  754.         if (type == TC_IF || type == TC_ELSE)
  755.         level--;
  756.         break;
  757.  
  758.     case TC_FOREACH:
  759.     case TC_WHILE:
  760.         if (type == TC_BREAK)
  761.         level++;
  762.         break;
  763.  
  764.     case TC_END:
  765.         if (type == TC_BREAK)
  766.         level--;
  767.         break;
  768.  
  769.     case TC_SWITCH:
  770.         if (type == TC_SWITCH || type == TC_BRKSW)
  771.         level++;
  772.         break;
  773.  
  774.     case TC_ENDSW:
  775.         if (type == TC_SWITCH || type == TC_BRKSW)
  776.         level--;
  777.         break;
  778.  
  779.     case TC_LABEL:
  780.         if (type == TC_GOTO && getword(aword) && eq(aword, goal))
  781.         level = -1;
  782.         break;
  783.  
  784.     default:
  785.         if (type != TC_GOTO && (type != TC_SWITCH || level != 0))
  786.         break;
  787.         if (lastchr(aword) != ':')
  788.         break;
  789.         aword[Strlen(aword) - 1] = 0;
  790.         if ((type == TC_GOTO && eq(aword, goal)) ||
  791.         (type == TC_SWITCH && eq(aword, STRdefault)))
  792.         level = -1;
  793.         break;
  794.  
  795.     case TC_CASE:
  796.         if (type != TC_SWITCH || level != 0)
  797.         break;
  798.         (void) getword(aword);
  799.         if (lastchr(aword) == ':')
  800.         aword[Strlen(aword) - 1] = 0;
  801.         cp = strip(Dfix1(aword));
  802.         if (Gmatch(goal, cp))
  803.         level = -1;
  804.         xfree((ptr_t) cp);
  805.         break;
  806.  
  807.     case TC_DEFAULT:
  808.         if (type == TC_SWITCH && level == 0)
  809.         level = -1;
  810.         break;
  811.     }
  812.     (void) getword(NULL);
  813.     } while (level >= 0);
  814. }
  815.  
  816. static int
  817. getword(wp)
  818.     register Char *wp;
  819. {
  820.     register int found = 0;
  821.     register int c, d;
  822.     int     kwd = 0;
  823.     Char   *owp = wp;
  824.  
  825.     c = readc(1);
  826.     d = 0;
  827.     do {
  828.     while (c == ' ' || c == '\t')
  829.         c = readc(1);
  830.     if (c == '#')
  831.         do
  832.         c = readc(1);
  833.         while (c >= 0 && c != '\n');
  834.     if (c < 0)
  835.         goto past;
  836.     if (c == '\n') {
  837.         if (wp)
  838.         break;
  839.         return (0);
  840.     }
  841.     unreadc(c);
  842.     found = 1;
  843.     do {
  844.         c = readc(1);
  845.         if (c == '\\' && (c = readc(1)) == '\n')
  846.         c = ' ';
  847.         if (c == '\'' || c == '"')
  848.         if (d == 0)
  849.             d = c;
  850.         else if (d == c)
  851.             d = 0;
  852.         if (c < 0)
  853.         goto past;
  854.         if (wp) {
  855.         *wp++ = (Char) c;
  856.         *wp = 0;    /* end the string b4 test */
  857.         }
  858.     } while ((d || (!(kwd = keyword(owp)) && c != ' '
  859.           && c != '\t')) && c != '\n');
  860.     } while (wp == 0);
  861.  
  862.     /*
  863.      * if we have read a keyword ( "if", "switch" or "while" ) then we do not
  864.      * need to unreadc the look-ahead char
  865.      */
  866.     if (!kwd) {
  867.     unreadc(c);
  868.     if (found)
  869.         *--wp = 0;
  870.     }
  871.  
  872.     return (found);
  873.  
  874. past:
  875.     switch (Stype) {
  876.  
  877.     case TC_IF:
  878.     stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
  879.     break;
  880.  
  881.     case TC_ELSE:
  882.     stderror(ERR_NAME | ERR_NOTFOUND, "endif");
  883.     break;
  884.  
  885.     case TC_BRKSW:
  886.     case TC_SWITCH:
  887.     stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
  888.     break;
  889.  
  890.     case TC_BREAK:
  891.     stderror(ERR_NAME | ERR_NOTFOUND, "end");
  892.     break;
  893.  
  894.     case TC_GOTO:
  895.     setname(short2str(Sgoal));
  896.     stderror(ERR_NAME | ERR_NOTFOUND, "label");
  897.     break;
  898.  
  899.     default:
  900.     break;
  901.     }
  902.     /* NOTREACHED */
  903.     return (0);
  904. }
  905.  
  906. /*
  907.  * keyword(wp) determines if wp is one of the built-n functions if,
  908.  * switch or while. It seems that when an if statement looks like
  909.  * "if(" then getword above sucks in the '(' and so the search routine
  910.  * never finds what it is scanning for. Rather than rewrite doword, I hack
  911.  * in a test to see if the string forms a keyword. Then doword stops
  912.  * and returns the word "if" -strike
  913.  */
  914.  
  915. static int
  916. keyword(wp)
  917.     Char   *wp;
  918. {
  919.     static Char STRif[] = {'i', 'f', '\0'};
  920.     static Char STRwhile[] = {'w', 'h', 'i', 'l', 'e', '\0'};
  921.     static Char STRswitch[] = {'s', 'w', 'i', 't', 'c', 'h', '\0'};
  922.  
  923.     if (!wp)
  924.     return (0);
  925.  
  926.     if ((Strcmp(wp, STRif) == 0) || (Strcmp(wp, STRwhile) == 0)
  927.     || (Strcmp(wp, STRswitch) == 0))
  928.     return (1);
  929.  
  930.     return (0);
  931. }
  932.  
  933. static void
  934. toend()
  935. {
  936.     if (whyles->w_end.type == F_SEEK && whyles->w_end.f_seek == 0) {
  937.     search(TC_BREAK, 0, NULL);
  938.     btell(&whyles->w_end);
  939.     whyles->w_end.f_seek--;
  940.     }
  941.     else {
  942.     bseek(&whyles->w_end);
  943.     }
  944.     wfree();
  945. }
  946.  
  947. void
  948. wfree()
  949. {
  950.     struct Ain    o;
  951.     struct whyle *nwp;
  952. #ifdef lint
  953.     nwp = NULL;    /* sun lint is dumb! */
  954. #endif
  955.  
  956. #ifdef FDEBUG
  957.     static char foo[] = "IAFE";
  958. #endif /* FDEBUG */
  959.  
  960.     btell(&o);
  961.  
  962. #ifdef FDEBUG
  963.     xprintf("o->type %c o->a_seek %d o->f_seek %d\n", 
  964.         foo[o.type + 1], o.a_seek, o.f_seek);
  965. #endif /* FDEBUG */
  966.  
  967.     for (; whyles; whyles = nwp) {
  968.     register struct whyle *wp = whyles;
  969.     nwp = wp->w_next;
  970.  
  971. #ifdef FDEBUG
  972.     xprintf("start->type %c start->a_seek %d start->f_seek %d\n", 
  973.         foo[wp->w_start.type+1], 
  974.         wp->w_start.a_seek, wp->w_start.f_seek);
  975.     xprintf("end->type %c end->a_seek %d end->f_seek %d\n", 
  976.         foo[wp->w_end.type + 1], wp->w_end.a_seek, wp->w_end.f_seek);
  977. #endif /* FDEBUG */
  978.  
  979.     /*
  980.      * XXX: We free loops that have different seek types.
  981.      */
  982.     if (wp->w_end.type != I_SEEK && wp->w_start.type == wp->w_end.type &&
  983.         wp->w_start.type == o.type) {
  984.         if (wp->w_end.type == F_SEEK) {
  985.         if (o.f_seek >= wp->w_start.f_seek && 
  986.             (wp->w_end.f_seek == 0 || o.f_seek < wp->w_end.f_seek))
  987.             break;
  988.         }
  989.         else {
  990.         if (o.a_seek >= wp->w_start.a_seek && 
  991.             (wp->w_end.a_seek == 0 || o.a_seek < wp->w_end.a_seek))
  992.             break;
  993.         }
  994.     }
  995.  
  996.     if (wp->w_fe0)
  997.         blkfree(wp->w_fe0);
  998.     if (wp->w_fename)
  999.         xfree((ptr_t) wp->w_fename);
  1000.     xfree((ptr_t) wp);
  1001.     }
  1002. }
  1003.  
  1004. /*ARGSUSED*/
  1005. void
  1006. doecho(v, c)
  1007.     Char  **v;
  1008.     struct command *c;
  1009. {
  1010.     xecho(' ', v);
  1011. }
  1012.  
  1013. /*ARGSUSED*/
  1014. void
  1015. doglob(v, c)
  1016.     Char  **v;
  1017.     struct command *c;
  1018. {
  1019.     xecho(0, v);
  1020.     flush();
  1021. }
  1022.  
  1023. static void
  1024. xecho(sep, v)
  1025.     int    sep;
  1026.     register Char **v;
  1027. {
  1028.     register Char *cp;
  1029.     int     nonl = 0;
  1030. #ifdef ECHO_STYLE
  1031.     int        echo_style = ECHO_STYLE;
  1032. #else /* !ECHO_STYLE */
  1033. # if SYSVREL > 0
  1034.     int        echo_style = SYSV_ECHO;
  1035. # else /* SYSVREL == 0 */
  1036.     int        echo_style = BSD_ECHO;
  1037. # endif /* SYSVREL */
  1038. #endif /* ECHO_STYLE */
  1039.     struct varent *vp;
  1040.  
  1041.     if ((vp = adrof(STRecho_style)) != NULL && vp->vec != NULL &&
  1042.     vp->vec[0] != NULL) {
  1043.     if (Strcmp(vp->vec[0], STRbsd) == 0)
  1044.         echo_style = BSD_ECHO;
  1045.     else if (Strcmp(vp->vec[0], STRsysv) == 0)
  1046.         echo_style = SYSV_ECHO;
  1047.     else if (Strcmp(vp->vec[0], STRboth) == 0)
  1048.         echo_style = BOTH_ECHO;
  1049.     else if (Strcmp(vp->vec[0], STRnone) == 0)
  1050.         echo_style = NONE_ECHO;
  1051.     }
  1052.  
  1053.     if (setintr)
  1054. #ifdef BSDSIGS
  1055.     (void) sigsetmask(sigblock((sigmask_t) 0) & ~sigmask(SIGINT));
  1056. #else /* !BSDSIGS */
  1057.     (void) sigrelse (SIGINT);
  1058. #endif /* BSDSIGS */
  1059.     v++;
  1060.     if (*v == 0)
  1061.     return;
  1062.     gflag = 0, tglob(v);
  1063.     if (gflag) {
  1064.     v = globall(v);
  1065.     if (v == 0)
  1066.         stderror(ERR_NAME | ERR_NOMATCH);
  1067.     }
  1068.     else {
  1069.     v = gargv = saveblk(v);
  1070.     trim(v);
  1071.     }
  1072.  
  1073.     if ((echo_style & BSD_ECHO) != 0 && sep == ' ' && *v && eq(*v, STRmn))
  1074.     nonl++, v++;
  1075.  
  1076.     while ((cp = *v++) != 0) {
  1077.     register int c;
  1078.  
  1079.     while ((c = *cp++) != 0) {
  1080.         if ((echo_style & SYSV_ECHO) != 0 && c == '\\') {
  1081.         switch (c = *cp++) {
  1082.         case 'b':
  1083.             c = '\b';
  1084.             break;
  1085.         case 'c':
  1086.             nonl = 1;
  1087.             goto done;
  1088.         case 'f':
  1089.             c = '\f';
  1090.             break;
  1091.         case 'n':
  1092.             c = '\n';
  1093.             break;
  1094.         case 'r':
  1095.             c = '\r';
  1096.             break;
  1097.         case 't':
  1098.             c = '\t';
  1099.             break;
  1100.         case 'v':
  1101.             c = '\v';
  1102.             break;
  1103.         case '\\':
  1104.             c = '\\';
  1105.             break;
  1106.         case '0':
  1107.             c = 0;
  1108.             if (*cp >= '0' && *cp < '8')
  1109.             c = c * 8 + *cp++ - '0';
  1110.             if (*cp >= '0' && *cp < '8')
  1111.             c = c * 8 + *cp++ - '0';
  1112.             if (*cp >= '0' && *cp < '8')
  1113.             c = c * 8 + *cp++ - '0';
  1114.             break;
  1115.         case '\0':
  1116.             c = *--cp;
  1117.             break;
  1118.         default:
  1119.             xputchar('\\' | QUOTE);
  1120.             break;
  1121.         }
  1122.         }
  1123.         xputchar(c | QUOTE);
  1124.  
  1125.     }
  1126.     if (*v)
  1127.         xputchar(sep | QUOTE);
  1128.     }
  1129. done:
  1130.     if (sep && nonl == 0)
  1131.     xputchar('\n');
  1132.     else
  1133.     flush();
  1134.     if (setintr)
  1135. #ifdef BSDSIGS
  1136.     (void) sigblock(sigmask(SIGINT));
  1137. #else /* !BSDSIGS */
  1138.     (void) sighold(SIGINT);
  1139. #endif /* BSDSIGS */
  1140.     if (gargv)
  1141.     blkfree(gargv), gargv = 0;
  1142. }
  1143.  
  1144. /* check whether an environment variable should invoke 'set_locale()' */
  1145. static bool islocale_var(var)
  1146. register Char *var;
  1147. {
  1148.     static Char *locale_vars[] = {
  1149.     STRLANG,    STRLC_CTYPE,    STRLC_NUMERIC,    STRLC_TIME,
  1150.     STRLC_COLLATE,    STRLC_MESSAGES,    STRLC_MONETARY, 0
  1151.     };
  1152.     register Char **v;
  1153.  
  1154.     for (v = locale_vars; *v; ++v)
  1155.     if (eq(var, *v))
  1156.         return 1;
  1157.     return 0;
  1158. }
  1159.  
  1160. /* from "Karl Berry." <karl%mote.umb.edu@relay.cs.net> -- for NeXT things
  1161.    (and anything else with a modern compiler) */
  1162.  
  1163. /*ARGSUSED*/
  1164. void
  1165. dosetenv(v, c)
  1166.     register Char **v;
  1167.     struct command *c;
  1168. {
  1169.     Char   *vp, *lp;
  1170.  
  1171.     v++;
  1172.     if ((vp = *v++) == 0) {
  1173.     register Char **ep;
  1174.  
  1175.     if (setintr)
  1176. #ifdef BSDSIGS
  1177.         (void) sigsetmask(sigblock((sigmask_t) 0) & ~sigmask(SIGINT));
  1178. #else /* !BSDSIGS */
  1179.         (void) sigrelse (SIGINT);
  1180. #endif /* BSDSIGS */
  1181.     for (ep = STR_environ; *ep; ep++)
  1182.         xprintf("%S\n", *ep);
  1183.     return;
  1184.     }
  1185.     if ((lp = *v++) == 0)
  1186.     lp = STRNULL;
  1187.  
  1188.     tsetenv(vp, lp = globone(lp, G_APPEND));
  1189.     if (eq(vp, STRKPATH)) {
  1190.     importpath(lp);
  1191.     dohash(NULL, NULL);
  1192.     }
  1193. #ifdef apollo
  1194.     else if (eq(vp, STRSYSTYPE))
  1195.     dohash(NULL, NULL);
  1196. #endif /* apollo */
  1197.     else if (islocale_var(vp)) {
  1198. #ifdef NLS
  1199.     int     k;
  1200.  
  1201. # ifdef SETLOCALEBUG
  1202.     dont_free = 1;
  1203. # endif /* SETLOCALEBUG */
  1204.     (void) setlocale(LC_ALL, "");
  1205. # ifdef LC_COLLATE
  1206.     (void) setlocale(LC_COLLATE, "");
  1207. # endif
  1208. # ifdef SETLOCALEBUG
  1209.     dont_free = 0;
  1210. # endif /* SETLOCALEBUG */
  1211. # ifdef STRCOLLBUG
  1212.     fix_strcoll_bug();
  1213. # endif /* STRCOLLBUG */
  1214.     tw_cmd_free();    /* since the collation sequence has changed */
  1215.     for (k = 0200; k <= 0377 && !Isprint(k); k++)
  1216.         continue;
  1217.     AsciiOnly = k > 0377;
  1218. #else /* !NLS */
  1219.     AsciiOnly = 0;
  1220. #endif /* NLS */
  1221.     NLSMapsAreInited = 0;
  1222.     ed_Init();
  1223.     if (MapsAreInited && !NLSMapsAreInited)
  1224.         ed_InitNLSMaps();
  1225.     }
  1226.     else if (eq(vp, STRNOREBIND)) {
  1227.     NoNLSRebind = 1;
  1228.     }
  1229.     else if (eq(vp, STRKTERM)) {
  1230.     set(STRterm, Strsave(lp));
  1231.     GotTermCaps = 0;
  1232.     ed_Init();
  1233.     }
  1234.     else if (eq(vp, STRKHOME)) {
  1235.      Char *cp = Strsave(value(vp));    /* get the old value back */
  1236.  
  1237.     /*
  1238.      * convert to canonical pathname (possibly resolving symlinks)
  1239.      */
  1240.     cp = dcanon(cp, cp);
  1241.  
  1242.     set(STRhome, Strsave(cp));    /* have to save the new val */
  1243.  
  1244.     /* fix directory stack for new tilde home */
  1245.     dtilde();
  1246.     xfree((ptr_t) cp);
  1247.     }
  1248.     else if (eq(vp, STRKSHLVL)) 
  1249.     set(STRshlvl, Strsave(lp));
  1250.     else if (eq(vp, STRKUSER))
  1251.     set(STRuser, Strsave(lp));
  1252. #ifdef SIG_WINDOW
  1253.     /*
  1254.      * Load/Update $LINES $COLUMNS
  1255.      */
  1256.     else if ((eq(lp, STRNULL) &&
  1257.           (eq(vp, STRLINES) || eq(vp, STRCOLUMNS))) ||
  1258.          eq(vp, STRTERMCAP)) {
  1259.     check_window_size(1);
  1260.     }
  1261.     /*
  1262.      * Change the size to the one directed by $LINES and $COLUMNS
  1263.      */
  1264.     else if (eq(vp, STRLINES) || eq(vp, STRCOLUMNS)) {
  1265.     GotTermCaps = 0;
  1266.     ed_Init();
  1267.     }
  1268. #endif /* SIG_WINDOW */
  1269.     xfree((ptr_t) lp);
  1270. }
  1271.  
  1272. /*ARGSUSED*/
  1273. void
  1274. dounsetenv(v, c)
  1275.     register Char **v;
  1276.     struct command *c;
  1277. {
  1278.     Char  **ep, *p, *n;
  1279.     int     i, maxi;
  1280.     static Char *name = NULL;
  1281.  
  1282.     if (name)
  1283.     xfree((ptr_t) name);
  1284.     /*
  1285.      * Find the longest environment variable
  1286.      */
  1287.     for (maxi = 0, ep = STR_environ; *ep; ep++) {
  1288.     for (i = 0, p = *ep; *p && *p != '='; p++, i++)
  1289.         continue;
  1290.     if (i > maxi)
  1291.         maxi = i;
  1292.     }
  1293.  
  1294.     name = (Char *) xmalloc((size_t) ((maxi + 1) * sizeof(Char)));
  1295.  
  1296.     while (++v && *v) 
  1297.     for (maxi = 1; maxi;)
  1298.         for (maxi = 0, ep = STR_environ; *ep; ep++) {
  1299.         for (n = name, p = *ep; *p && *p != '='; *n++ = *p++)
  1300.             continue;
  1301.         *n = '\0';
  1302.         if (!Gmatch(name, *v))
  1303.             continue;
  1304.         maxi = 1;
  1305.  
  1306.         /* Unset the name. This wasn't being done until
  1307.          * later but most of the stuff following won't
  1308.          * work (particularly the setlocale() and getenv()
  1309.          * stuff) as intended until the name is actually
  1310.          * removed. (sg)
  1311.          */
  1312.         Unsetenv(name);
  1313.  
  1314.         if (eq(name, STRNOREBIND))
  1315.             NoNLSRebind = 0;
  1316. #ifdef apollo
  1317.         else if (eq(name, STRSYSTYPE))
  1318.             dohash(NULL, NULL);
  1319. #endif /* apollo */
  1320.         else if (islocale_var(name)) {
  1321. #ifdef NLS
  1322.             int     k;
  1323.  
  1324. # ifdef SETLOCALEBUG
  1325.             dont_free = 1;
  1326. # endif /* SETLOCALEBUG */
  1327.             (void) setlocale(LC_ALL, "");
  1328. # ifdef LC_COLLATE
  1329.             (void) setlocale(LC_COLLATE, "");
  1330. # endif
  1331. # ifdef SETLOCALEBUG
  1332.             dont_free = 0;
  1333. # endif /* SETLOCALEBUG */
  1334. # ifdef STRCOLLBUG
  1335.             fix_strcoll_bug();
  1336. # endif /* STRCOLLBUG */
  1337.             tw_cmd_free();/* since the collation sequence has changed */
  1338.             for (k = 0200; k <= 0377 && !Isprint(k); k++)
  1339.             continue;
  1340.             AsciiOnly = k > 0377;
  1341. #else /* !NLS */
  1342.             AsciiOnly = getenv("LANG") == NULL &&
  1343.             getenv("LC_CTYPE") == NULL;
  1344. #endif /* NLS */
  1345.             NLSMapsAreInited = 0;
  1346.             ed_Init();
  1347.             if (MapsAreInited && !NLSMapsAreInited)
  1348.             ed_InitNLSMaps();
  1349.  
  1350.         }
  1351.         /*
  1352.          * start again cause the environment changes
  1353.          */
  1354.         break;
  1355.         }
  1356.     xfree((ptr_t) name); name = NULL;
  1357. }
  1358.  
  1359. void
  1360. tsetenv(name, val)
  1361.     Char   *name, *val;
  1362. {
  1363. #ifdef SETENV_IN_LIB
  1364. /*
  1365.  * XXX: This does not work right, since tcsh cannot track changes to
  1366.  * the environment this way. (the builtin setenv without arguments does
  1367.  * not print the right stuff neither does unsetenv). This was for Mach,
  1368.  * it is not needed anymore.
  1369.  */
  1370. #undef setenv
  1371.     char    nameBuf[BUFSIZE];
  1372.     char   *cname = short2str(name);
  1373.  
  1374.     if (cname == NULL)
  1375.     return;
  1376.     (void) strcpy(nameBuf, cname);
  1377.     setenv(nameBuf, short2str(val), 1);
  1378. #else /* !SETENV_IN_LIB */
  1379.     register Char **ep = STR_environ;
  1380.     register Char *cp, *dp;
  1381.     Char   *blk[2];
  1382.     Char  **oep = ep;
  1383.  
  1384.  
  1385.     for (; *ep; ep++) {
  1386.     for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
  1387.         continue;
  1388.     if (*cp != 0 || *dp != '=')
  1389.         continue;
  1390.     cp = Strspl(STRequal, val);
  1391.     xfree((ptr_t) * ep);
  1392.     *ep = strip(Strspl(name, cp));
  1393.     xfree((ptr_t) cp);
  1394.     blkfree((Char **) environ);
  1395.     environ = short2blk(STR_environ);
  1396.     return;
  1397.     }
  1398.     cp = Strspl(name, STRequal);
  1399.     blk[0] = strip(Strspl(cp, val));
  1400.     xfree((ptr_t) cp);
  1401.     blk[1] = 0;
  1402.     STR_environ = blkspl(STR_environ, blk);
  1403.     blkfree((Char **) environ);
  1404.     environ = short2blk(STR_environ);
  1405.     xfree((ptr_t) oep);
  1406. #endif /* SETENV_IN_LIB */
  1407. }
  1408.  
  1409. void
  1410. Unsetenv(name)
  1411.     Char   *name;
  1412. {
  1413.     register Char **ep = STR_environ;
  1414.     register Char *cp, *dp;
  1415.     Char  **oep = ep;
  1416.  
  1417.     for (; *ep; ep++) {
  1418.     for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
  1419.         continue;
  1420.     if (*cp != 0 || *dp != '=')
  1421.         continue;
  1422.     cp = *ep;
  1423.     *ep = 0;
  1424.     STR_environ = blkspl(STR_environ, ep + 1);
  1425.     environ = short2blk(STR_environ);
  1426.     *ep = cp;
  1427.     xfree((ptr_t) cp);
  1428.     xfree((ptr_t) oep);
  1429.     return;
  1430.     }
  1431. }
  1432.  
  1433. /*ARGSUSED*/
  1434. void
  1435. doumask(v, c)
  1436.     register Char **v;
  1437.     struct command *c;
  1438. {
  1439.     register Char *cp = v[1];
  1440.     register int i;
  1441.  
  1442.     if (cp == 0) {
  1443.     i = umask(0);
  1444.     (void) umask(i);
  1445.     xprintf("%o\n", i);
  1446.     return;
  1447.     }
  1448.     i = 0;
  1449.     while (Isdigit(*cp) && *cp != '8' && *cp != '9')
  1450.     i = i * 8 + *cp++ - '0';
  1451.     if (*cp || i < 0 || i > 0777)
  1452.     stderror(ERR_NAME | ERR_MASK);
  1453.     (void) umask(i);
  1454. }
  1455.  
  1456. #ifndef HAVENOLIMIT
  1457. # ifndef BSDLIMIT
  1458.    typedef long RLIM_TYPE;
  1459. #  ifndef RLIM_INFINITY
  1460. #   ifndef _MINIX
  1461.     extern RLIM_TYPE ulimit();
  1462. #   endif /* ! _MINIX */
  1463. #   define RLIM_INFINITY 0x003fffff
  1464. #   define RLIMIT_FSIZE 1
  1465. #  endif /* RLIM_INFINITY */
  1466. #  ifdef aiws
  1467. #   define toset(a) (((a) == 3) ? 1004 : (a) + 1)
  1468. #   define RLIMIT_DATA    3
  1469. #   define RLIMIT_STACK 1005
  1470. #  else /* aiws */
  1471. #   define toset(a) ((a) + 1)
  1472. #  endif /* aiws */
  1473. # else /* BSDLIMIT */
  1474. #  if defined(BSD4_4) && !defined(__386BSD__)
  1475.     typedef quad_t RLIM_TYPE;
  1476. #  else
  1477.     typedef int RLIM_TYPE;
  1478. #  endif /* BSD4_4 && !__386BSD__ */
  1479. # endif /* BSDLIMIT */
  1480.  
  1481. # if defined(hpux) && defined(BSDLIMIT)
  1482. /* Yes hpux8.0 has limits but <sys/resource.h> does not make them public */
  1483. /* Yes, we could have defined _KERNEL, and -I/etc/conf/h, but is that better? */
  1484. #  ifndef RLIMIT_CPU
  1485. #   define RLIMIT_CPU        0
  1486. #   define RLIMIT_FSIZE        1
  1487. #   define RLIMIT_DATA        2
  1488. #   define RLIMIT_STACK        3
  1489. #   define RLIMIT_CORE        4
  1490. #   define RLIMIT_RSS        5
  1491. #   define RLIMIT_NOFILE    6
  1492. #  endif /* RLIMIT_CPU */
  1493. #  ifndef RLIM_INFINITY
  1494. #   define RLIM_INFINITY    0x7fffffff
  1495. #  endif /* RLIM_INFINITY */
  1496. # endif /* hpux && BSDLIMIT */
  1497.  
  1498. struct limits limits[] = 
  1499. {
  1500. # ifdef RLIMIT_CPU
  1501.     RLIMIT_CPU,     "cputime",    1,    "seconds",
  1502. # endif /* RLIMIT_CPU */
  1503.  
  1504. # ifdef RLIMIT_FSIZE
  1505. #  ifndef aiws
  1506.     RLIMIT_FSIZE,     "filesize",    1024,    "kbytes",
  1507. #  else
  1508.     RLIMIT_FSIZE,     "filesize",    512,    "blocks",
  1509. #  endif /* aiws */
  1510. # endif /* RLIMIT_FSIZE */
  1511.  
  1512. # ifdef RLIMIT_DATA
  1513.     RLIMIT_DATA,     "datasize",    1024,    "kbytes",
  1514. # endif /* RLIMIT_DATA */
  1515.  
  1516. # ifdef RLIMIT_STACK
  1517. #  ifndef aiws
  1518.     RLIMIT_STACK,     "stacksize",    1024,    "kbytes",
  1519. #  else
  1520.     RLIMIT_STACK,     "stacksize",    1024 * 1024,    "kbytes",
  1521. #  endif /* aiws */
  1522. # endif /* RLIMIT_STACK */
  1523.  
  1524. # ifdef RLIMIT_CORE
  1525.     RLIMIT_CORE,     "coredumpsize",    1024,    "kbytes",
  1526. # endif /* RLIMIT_CORE */
  1527.  
  1528. # ifdef RLIMIT_RSS
  1529.     RLIMIT_RSS,     "memoryuse",    1024,    "kbytes",
  1530. # endif /* RLIMIT_RSS */
  1531.  
  1532. # ifdef RLIMIT_NOFILE
  1533.     RLIMIT_NOFILE,     "descriptors", 1,    "",
  1534. # endif /* RLIMIT_NOFILE */
  1535.  
  1536. # ifdef RLIMIT_CONCUR
  1537.     RLIMIT_CONCUR,     "concurrency", 1,    "thread(s)",
  1538. # endif /* RLIMIT_CONCUR */
  1539.  
  1540. # ifdef RLIMIT_MEMLOCK
  1541.     RLIMIT_MEMLOCK,    "memorylocked",    1024,    "kbytes",
  1542. # endif /* RLIMIT_MEMLOCK */
  1543.  
  1544. # ifdef RLIMIT_NPROC
  1545.     RLIMIT_NPROC,    "maxproc",    1,    "",
  1546. # endif /* RLIMIT_NPROC */
  1547.  
  1548. # ifdef RLIMIT_OFILE
  1549.     RLIMIT_OFILE,    "openfiles",    1,    "",
  1550. # endif /* RLIMIT_OFILE */
  1551.  
  1552.     -1,         NULL,         0,     NULL
  1553. };
  1554.  
  1555. static struct limits *findlim    __P((Char *));
  1556. static RLIM_TYPE getval        __P((struct limits *, Char **));
  1557. static void limtail        __P((Char *, char*));
  1558. static void plim        __P((struct limits *, int));
  1559. static int setlim        __P((struct limits *, int, RLIM_TYPE));
  1560.  
  1561. #ifdef convex
  1562. static  RLIM_TYPE
  1563. restrict_limit(value)
  1564.     double  value;
  1565. {
  1566.     /*
  1567.      * is f too large to cope with? return the maximum or minimum int
  1568.      */
  1569.     if (value > (double) INT_MAX)
  1570.     return (RLIM_TYPE) INT_MAX;
  1571.     else if (value < (double) INT_MIN)
  1572.     return (RLIM_TYPE) INT_MIN;
  1573.     else
  1574.     return (RLIM_TYPE) value;
  1575. }
  1576. #else /* !convex */
  1577. # define restrict_limit(x)    ((RLIM_TYPE) (x))
  1578. #endif /* convex */
  1579.  
  1580.  
  1581. static struct limits *
  1582. findlim(cp)
  1583.     Char   *cp;
  1584. {
  1585.     register struct limits *lp, *res;
  1586.  
  1587.     res = (struct limits *) NULL;
  1588.     for (lp = limits; lp->limconst >= 0; lp++)
  1589.     if (prefix(cp, str2short(lp->limname))) {
  1590.         if (res)
  1591.         stderror(ERR_NAME | ERR_AMBIG);
  1592.         res = lp;
  1593.     }
  1594.     if (res)
  1595.     return (res);
  1596.     stderror(ERR_NAME | ERR_LIMIT);
  1597.     /* NOTREACHED */
  1598.     return (0);
  1599. }
  1600.  
  1601. /*ARGSUSED*/
  1602. void
  1603. dolimit(v, c)
  1604.     register Char **v;
  1605.     struct command *c;
  1606. {
  1607.     register struct limits *lp;
  1608.     register RLIM_TYPE limit;
  1609.     int    hard = 0;
  1610.  
  1611.     v++;
  1612.     if (*v && eq(*v, STRmh)) {
  1613.     hard = 1;
  1614.     v++;
  1615.     }
  1616.     if (*v == 0) {
  1617.     for (lp = limits; lp->limconst >= 0; lp++)
  1618.         plim(lp, hard);
  1619.     return;
  1620.     }
  1621.     lp = findlim(v[0]);
  1622.     if (v[1] == 0) {
  1623.     plim(lp, hard);
  1624.     return;
  1625.     }
  1626.     limit = getval(lp, v + 1);
  1627.     if (setlim(lp, hard, limit) < 0)
  1628.     stderror(ERR_SILENT);
  1629. }
  1630.  
  1631. static  RLIM_TYPE
  1632. getval(lp, v)
  1633.     register struct limits *lp;
  1634.     Char  **v;
  1635. {
  1636.     register float f;
  1637. #ifndef atof    /* This can be a macro on linux */
  1638.     extern double  atof __P((const char *));
  1639. #endif /* atof */
  1640.     Char   *cp = *v++;
  1641.  
  1642.     f = atof(short2str(cp));
  1643.  
  1644. # ifdef convex
  1645.     /*
  1646.      * is f too large to cope with. limit f to minint, maxint  - X-6768 by
  1647.      * strike
  1648.      */
  1649.     if ((f < (double) INT_MIN) || (f > (double) INT_MAX)) {
  1650.     stderror(ERR_NAME | ERR_TOOLARGE);
  1651.     }
  1652. # endif /* convex */
  1653.  
  1654.     while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E')
  1655.     cp++;
  1656.     if (*cp == 0) {
  1657.     if (*v == 0)
  1658.         return restrict_limit((f + 0.5) * lp->limdiv);
  1659.     cp = *v;
  1660.     }
  1661.     switch (*cp) {
  1662. # ifdef RLIMIT_CPU
  1663.     case ':':
  1664.     if (lp->limconst != RLIMIT_CPU)
  1665.         goto badscal;
  1666.     return restrict_limit((f * 60.0 + atof(short2str(cp + 1))));
  1667.     case 'h':
  1668.     if (lp->limconst != RLIMIT_CPU)
  1669.         goto badscal;
  1670.     limtail(cp, "hours");
  1671.     f *= 3600.0;
  1672.     break;
  1673.     case 'm':
  1674.     if (lp->limconst == RLIMIT_CPU) {
  1675.         limtail(cp, "minutes");
  1676.         f *= 60.0;
  1677.         break;
  1678.     }
  1679.     *cp = 'm';
  1680.     limtail(cp, "megabytes");
  1681.     f *= 1024.0 * 1024.0;
  1682.     break;
  1683.     case 's':
  1684.     if (lp->limconst != RLIMIT_CPU)
  1685.         goto badscal;
  1686.     limtail(cp, "seconds");
  1687.     break;
  1688. # endif /* RLIMIT_CPU */
  1689.     case 'M':
  1690. # ifdef RLIMIT_CPU
  1691.     if (lp->limconst == RLIMIT_CPU)
  1692.         goto badscal;
  1693. # endif /* RLIMIT_CPU */
  1694.     *cp = 'm';
  1695.     limtail(cp, "megabytes");
  1696.     f *= 1024.0 * 1024.0;
  1697.     break;
  1698.     case 'k':
  1699. # ifdef RLIMIT_CPU
  1700.     if (lp->limconst == RLIMIT_CPU)
  1701.         goto badscal;
  1702. # endif /* RLIMIT_CPU */
  1703.     limtail(cp, "kbytes");
  1704.     f *= 1024.0;
  1705.     break;
  1706.     case 'b':
  1707. # ifdef RLIMIT_CPU
  1708.     if (lp->limconst == RLIMIT_CPU)
  1709.         goto badscal;
  1710. # endif /* RLIMIT_CPU */
  1711.     limtail(cp, "blocks");
  1712.     f *= 512.0;
  1713.     break;
  1714.     case 'u':
  1715.     limtail(cp, "unlimited");
  1716.     return (RLIM_INFINITY);
  1717.     default:
  1718. # ifdef RLIMIT_CPU
  1719. badscal:
  1720. # endif /* RLIMIT_CPU */
  1721.     stderror(ERR_NAME | ERR_SCALEF);
  1722.     }
  1723. # ifdef convex
  1724.     return restrict_limit((f + 0.5));
  1725. # else
  1726.     f += 0.5;
  1727.     if (f > (float) RLIM_INFINITY)
  1728.     return RLIM_INFINITY;
  1729.     else
  1730.     return ((RLIM_TYPE) f);
  1731. # endif /* convex */
  1732. }
  1733.  
  1734. static void
  1735. limtail(cp, str)
  1736.     Char   *cp;
  1737.     char   *str;
  1738. {
  1739.     while (*cp && *cp == *str)
  1740.     cp++, str++;
  1741.     if (*cp)
  1742.     stderror(ERR_BADSCALE, str);
  1743. }
  1744.  
  1745.  
  1746. /*ARGSUSED*/
  1747. static void
  1748. plim(lp, hard)
  1749.     register struct limits *lp;
  1750.     int hard;
  1751. {
  1752. # ifdef BSDLIMIT
  1753.     struct rlimit rlim;
  1754. # endif /* BSDLIMIT */
  1755.     RLIM_TYPE limit;
  1756.     int     div = lp->limdiv;
  1757.  
  1758.     xprintf("%s \t", lp->limname);
  1759.  
  1760. # ifndef BSDLIMIT
  1761.     limit = ulimit(lp->limconst, 0);
  1762. #  ifdef aiws
  1763.     if (lp->limconst == RLIMIT_DATA)
  1764.     limit -= 0x20000000;
  1765. #  endif /* aiws */
  1766. # else /* BSDLIMIT */
  1767.     (void) getrlimit(lp->limconst, &rlim);
  1768.     limit = hard ? rlim.rlim_max : rlim.rlim_cur;
  1769. # endif /* BSDLIMIT */
  1770.  
  1771. # if !defined(BSDLIMIT) || defined(hpux)
  1772.     /*
  1773.      * Christos: filesize comes in 512 blocks. we divide by 2 to get 1024
  1774.      * blocks. Note we cannot pre-multiply cause we might overflow (A/UX)
  1775.      */
  1776.     if (lp->limconst == RLIMIT_FSIZE) {
  1777.     if (limit >= (RLIM_INFINITY / 512))
  1778.         limit = RLIM_INFINITY;
  1779.     else
  1780.         div = (div == 1024 ? 2 : 1);
  1781.     }
  1782. # endif /* !BSDLIMIT || hpux */
  1783.  
  1784.     if (limit == RLIM_INFINITY)
  1785.     xprintf("unlimited");
  1786.     else
  1787. # ifdef RLIMIT_CPU
  1788.     if (lp->limconst == RLIMIT_CPU)
  1789.     psecs((long) limit);
  1790.     else
  1791. # endif /* RLIMIT_CPU */
  1792.     xprintf("%ld %s", (long) (limit / div), lp->limscale);
  1793.     xputchar('\n');
  1794. }
  1795.  
  1796. /*ARGSUSED*/
  1797. void
  1798. dounlimit(v, c)
  1799.     register Char **v;
  1800.     struct command *c;
  1801. {
  1802.     register struct limits *lp;
  1803.     int     lerr = 0;
  1804.     int    hard = 0;
  1805.  
  1806.     v++;
  1807.     if (*v && eq(*v, STRmh)) {
  1808.     hard = 1;
  1809.     v++;
  1810.     }
  1811.     if (*v == 0) {
  1812.     for (lp = limits; lp->limconst >= 0; lp++)
  1813.         if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0)
  1814.         lerr++;
  1815.     if (lerr)
  1816.         stderror(ERR_SILENT);
  1817.     return;
  1818.     }
  1819.     while (*v) {
  1820.     lp = findlim(*v++);
  1821.     if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0)
  1822.         stderror(ERR_SILENT);
  1823.     }
  1824. }
  1825.  
  1826. static int
  1827. setlim(lp, hard, limit)
  1828.     register struct limits *lp;
  1829.     int    hard;
  1830.     RLIM_TYPE limit;
  1831. {
  1832. # ifdef BSDLIMIT
  1833.     struct rlimit rlim;
  1834.  
  1835.     (void) getrlimit(lp->limconst, &rlim);
  1836.  
  1837. #  ifdef hpux
  1838.     /* Even though hpux has setrlimit(), it expects fsize in 512 byte blocks */
  1839.     if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
  1840.     limit /= 512;
  1841. #  endif /* hpux */
  1842.     if (hard)
  1843.     rlim.rlim_max = limit;
  1844.     else if (limit == RLIM_INFINITY && euid != 0)
  1845.     rlim.rlim_cur = rlim.rlim_max;
  1846.     else
  1847.     rlim.rlim_cur = limit;
  1848.  
  1849.     if (setrlimit(lp->limconst, &rlim) < 0) {
  1850. # else /* BSDLIMIT */
  1851.     if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
  1852.     limit /= 512;
  1853. # ifdef aiws
  1854.     if (lp->limconst == RLIMIT_DATA)
  1855.     limit += 0x20000000;
  1856. # endif /* aiws */
  1857.     if (ulimit(toset(lp->limconst), limit) < 0) {
  1858. # endif /* BSDLIMIT */
  1859.     xprintf("%s: %s: Can't %s%s limit\n", bname, lp->limname,
  1860.         limit == RLIM_INFINITY ? "remove" : "set",
  1861.         hard ? " hard" : "");
  1862.     return (-1);
  1863.     }
  1864.     return (0);
  1865. }
  1866.  
  1867. #endif /* !HAVENOLIMIT */
  1868.  
  1869. /*ARGSUSED*/
  1870. void
  1871. dosuspend(v, c)
  1872.     Char **v;
  1873.     struct command *c;
  1874. {
  1875. #ifdef BSDJOBS
  1876.     int     ctpgrp;
  1877.  
  1878.     sigret_t(*old) ();
  1879. #endif /* BSDJOBS */
  1880.  
  1881.     if (loginsh)
  1882.     stderror(ERR_SUSPLOG);
  1883.     untty();
  1884.  
  1885. #ifdef BSDJOBS
  1886.     old = signal(SIGTSTP, SIG_DFL);
  1887.     (void) kill(0, SIGTSTP);
  1888.     /* the shell stops here */
  1889.     (void) signal(SIGTSTP, old);
  1890. #else /* !BSDJOBS */
  1891.     stderror(ERR_JOBCONTROL);
  1892. #endif /* BSDJOBS */
  1893.  
  1894. #ifdef BSDJOBS
  1895.     if (tpgrp != -1) {
  1896. retry:
  1897.     ctpgrp = tcgetpgrp(FSHTTY);
  1898.     if (ctpgrp != opgrp) {
  1899.         old = signal(SIGTTIN, SIG_DFL);
  1900.         (void) kill(0, SIGTTIN);
  1901.         (void) signal(SIGTTIN, old);
  1902.         goto retry;
  1903.     }
  1904.     (void) setpgid(0, shpgrp);
  1905.     (void) tcsetpgrp(FSHTTY, shpgrp);
  1906.     }
  1907. #endif /* BSDJOBS */
  1908.     (void) setdisc(FSHTTY);
  1909. }
  1910.  
  1911. /* This is the dreaded EVAL built-in.
  1912.  *   If you don't fiddle with file descriptors, and reset didfds,
  1913.  *   this command will either ignore redirection inside or outside
  1914.  *   its aguments, e.g. eval "date >x"  vs.  eval "date" >x
  1915.  *   The stuff here seems to work, but I did it by trial and error rather
  1916.  *   than really knowing what was going on.  If tpgrp is zero, we are
  1917.  *   probably a background eval, e.g. "eval date &", and we want to
  1918.  *   make sure that any processes we start stay in our pgrp.
  1919.  *   This is also the case for "time eval date" -- stay in same pgrp.
  1920.  *   Otherwise, under stty tostop, processes will stop in the wrong
  1921.  *   pgrp, with no way for the shell to get them going again.  -IAN!
  1922.  */
  1923.  
  1924. static Char **gv = NULL, **gav = NULL;
  1925.  
  1926. /*ARGSUSED*/
  1927. void
  1928. doeval(v, c)
  1929.     Char  **v;
  1930.     struct command *c;
  1931. {
  1932.     Char  **oevalvec;
  1933.     Char   *oevalp;
  1934.     int     odidfds;
  1935. #ifndef CLOSE_ON_EXEC
  1936.     int     odidcch;
  1937. #endif /* CLOSE_ON_EXEC */
  1938.     jmp_buf_t osetexit;
  1939.     int     my_reenter;
  1940.     Char  **savegv;
  1941.     int     saveIN, saveOUT, saveDIAG;
  1942.     int     oSHIN, oSHOUT, oSHDIAG;
  1943.  
  1944.     oevalvec = evalvec;
  1945.     oevalp = evalp;
  1946.     odidfds = didfds;
  1947. #ifndef CLOSE_ON_EXEC
  1948.     odidcch = didcch;
  1949. #endif /* CLOSE_ON_EXEC */
  1950.     oSHIN = SHIN;
  1951.     oSHOUT = SHOUT;
  1952.     oSHDIAG = SHDIAG;
  1953.  
  1954.     savegv = gv;
  1955.     gav = v;
  1956.  
  1957.     gav++;
  1958.     if (*gav == 0)
  1959.     return;
  1960.     gflag = 0, tglob(gav);
  1961.     if (gflag) {
  1962.     gv = gav = globall(gav);
  1963.     gargv = 0;
  1964.     if (gav == 0)
  1965.         stderror(ERR_NOMATCH);
  1966.     gav = copyblk(gav);
  1967.     }
  1968.     else {
  1969.     gv = NULL;
  1970.     gav = copyblk(gav);
  1971.     trim(gav);
  1972.     }
  1973.  
  1974.     saveIN = dcopy(SHIN, -1);
  1975.     saveOUT = dcopy(SHOUT, -1);
  1976.     saveDIAG = dcopy(SHDIAG, -1);
  1977.  
  1978.     getexit(osetexit);
  1979.  
  1980.     /* PWP: setjmp/longjmp bugfix for optimizing compilers */
  1981. #ifdef cray
  1982.     my_reenter = 1;             /* assume non-zero return val */
  1983.     if (setexit() == 0) {
  1984.     my_reenter = 0;         /* Oh well, we were wrong */
  1985. #else /* !cray */
  1986.     if ((my_reenter = setexit()) == 0) {
  1987. #endif /* cray */
  1988.     evalvec = gav;
  1989.     evalp = 0;
  1990.     SHIN = dcopy(0, -1);
  1991.     SHOUT = dcopy(1, -1);
  1992.     SHDIAG = dcopy(2, -1);
  1993. #ifndef CLOSE_ON_EXEC
  1994.     didcch = 0;
  1995. #endif /* CLOSE_ON_EXEC */
  1996.     didfds = 0;
  1997.     process(0);
  1998.     }
  1999.  
  2000.     evalvec = oevalvec;
  2001.     evalp = oevalp;
  2002.     doneinp = 0;
  2003. #ifndef CLOSE_ON_EXEC
  2004.     didcch = odidcch;
  2005. #endif /* CLOSE_ON_EXEC */
  2006.     didfds = odidfds;
  2007.     (void) close(SHIN);
  2008.     (void) close(SHOUT);
  2009.     (void) close(SHDIAG);
  2010.     SHIN = dmove(saveIN, oSHIN);
  2011.     SHOUT = dmove(saveOUT, oSHOUT);
  2012.     SHDIAG = dmove(saveDIAG, oSHDIAG);
  2013.  
  2014.     if (gv)
  2015.     blkfree(gv);
  2016.  
  2017.     gv = savegv;
  2018.     resexit(osetexit);
  2019.     if (my_reenter)
  2020.     stderror(ERR_SILENT);
  2021. }
  2022.  
  2023. /*************************************************************************/
  2024. /* print list of builtin commands */
  2025.  
  2026. /*ARGSUSED*/
  2027. void
  2028. dobuiltins(v, c)
  2029. Char **v;
  2030. struct command *c;
  2031. {
  2032.     /* would use print_by_column() in tw.parse.c but that assumes
  2033.      * we have an array of Char * to pass.. (sg)
  2034.      */
  2035.     extern int Tty_raw_mode;
  2036.     extern int TermH;        /* from the editor routines */
  2037.     extern int lbuffed;        /* from sh.print.c */
  2038.  
  2039.     register struct biltins *b;
  2040.     register int row, col, columns, rows;
  2041.     unsigned int w, maxwidth;
  2042.  
  2043.     lbuffed = 0;        /* turn off line buffering */
  2044.  
  2045.     /* find widest string */
  2046.     for (maxwidth = 0, b = bfunc; b < &bfunc[nbfunc]; ++b)
  2047.     maxwidth = max(maxwidth, strlen(b->bname));
  2048.     ++maxwidth;                    /* for space */
  2049.  
  2050.     columns = (TermH + 1) / maxwidth;    /* PWP: terminal size change */
  2051.     if (!columns)
  2052.     columns = 1;
  2053.     rows = (nbfunc + (columns - 1)) / columns;
  2054.  
  2055.     for (b = bfunc, row = 0; row < rows; row++) {
  2056.     for (col = 0; col < columns; col++) {
  2057.         if (b < &bfunc[nbfunc]) {
  2058.         w = strlen(b->bname);
  2059.         xprintf("%s", b->bname);
  2060.         if (col < (columns - 1))    /* Not last column? */
  2061.             for (; w < maxwidth; w++)
  2062.             xputchar(' ');
  2063.         ++b;
  2064.         }
  2065.     }
  2066.     if (Tty_raw_mode)
  2067.         xputchar('\r');
  2068.     xputchar('\n');
  2069.     }
  2070.  
  2071.     lbuffed = 1;        /* turn back on line buffering */
  2072.     flush();
  2073. }
  2074.